# -*- coding:utf-8 -*-
"""
@Time : 2020-12-15 11:14
@Author: langengel
@Des: 用户鉴权
"""
from datetime import timedelta, datetime
import jwt
from fastapi import Depends, HTTPException, Request
from fastapi.security import SecurityScopes, OAuth2PasswordBearer
from jwt import PyJWTError
from pydantic import ValidationError
from starlette import status
from config.security import SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES
from models.base import User

OAuth2 = OAuth2PasswordBearer(tokenUrl="/login")


def create_access_token(data: dict):
    """
    创建token
    :param data: 加密数据
    :return: jwt
    """
    to_encode = data.copy()
    # token超时时间
    expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    # 向jwt加入超时时间
    to_encode.update({"exp": expire})
    # jwt加密
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

    return encoded_jwt


async def permissions(req: Request, security_scopes: SecurityScopes, token: str = Depends(OAuth2)):
    """
    权限验证
    :param req:
    :param token:
    :param security_scopes:
    :return:
    """

    try:
        # token解密
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        if payload:
            # 用户ID
            uid = payload.get("user", 0)
            # 用户类型
            is_admin = payload.get("is_admin", False)

            if uid == 0 or is_admin is None:
                credentials_exception = HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="无效凭证",
                    headers={"WWW-Authenticate": f"Bearer{token}"},
                )
                raise credentials_exception

        else:
            credentials_exception = HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="无效凭证",
                headers={"WWW-Authenticate": f"Bearer {token}"},
            )
            raise credentials_exception

    except jwt.ExpiredSignatureError:

        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="凭证已证过期",
            headers={"WWW-Authenticate": f"Bearer {token}"},
        )

    except jwt.InvalidTokenError:

        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="无效凭证",
            headers={"WWW-Authenticate": f"Bearer {token}"},
        )

    except (PyJWTError, ValidationError):

        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="无效凭证",
            headers={"WWW-Authenticate": f"Bearer {token}"},
        )
    # token验证 获取token登陆的用户数据

    # 判断是否设置了权限域
    if security_scopes.scopes:
        # 返回当前权限域
        authenticate_value = f'scope="{security_scopes.scope_str}"'
        # 用户权限域
        scopes = []
        # 非超级管理员
        if not is_admin:
            # 用户权限
            user_scopes = await user_permissions(uid)
            # 权限校验
            for scope in security_scopes.scopes:
                if scope not in user_scopes:
                    raise HTTPException(
                        status_code=status.HTTP_403_FORBIDDEN,
                        detail="Not permissions",
                        headers={"X-scopes": authenticate_value},
                    )
            # 缓存用户权限
            scopes = user_scopes
        # 缓存服务商用户权限
        req.state.scopes = scopes
    # 缓存用户ID
    req.state.uid = uid
    # 缓存用户类型
    req.state.is_admin = is_admin


async def user_permissions(uid: int):
    """
    获取用户所在服务商用户权限
    :param uid:
    :return: list 用户权限 ["add","delete"...]
    """
    # 查询用户
    user = await User().get_or_none(id=uid)
    # 获取管理员对应所在服务商对应角色 启用状态的角色
    roles = await user.roles.filter(role_status=True).all()

    # 用户权限
    user_rules = []
    for role in roles:
        # 获取单个角色对应权限域scopes
        rules = await role.rules.all().values('scopes')
        for rule in rules:
            # 权限加入
            user_rules.append(rule["scopes"])
    # 基本权限
    user_rules.append("user_info")
    return list(set(user_rules))
